<?php
/**
 * 反射类
 *
 * @version $Id: IReflection.php 312 2008-04-23 16:29:38Z iwind $
 * @package util.reflect
 * @since 0.0.3
 */

class IReflection extends IObject {
	/**
	 * Enter description here...
	 *
	 * @var ReflectionClass
	 */
	private $class;
	private $className;
	
	/**
	 * 构造
	 *
	 * @param string $className 类名称
	 * @since 0.0.3
	 */
	public function __construct($className) {
		$this->className = $className;
		$this->class = new ReflectionClass($className);
	}
	
	/**
	 * 取得类名称
	 *
	 * @return string
	 * @since 0.1
	 */
	public function getClassName() {
		return $this->className;
	}
	
	/**
	 * 取得反射类
	 *
	 * @return ReflectionClass
	 * @since 0.1
	 */
	public function getClass() {
		return $this->class;
	}
	
	/**
	 * 执行对象的某个方法
	 * 
	 * 并返回执行结果
	 *
	 * @param object $object 对象
	 * @param string $methodName 方法名
	 * @param array $arguments 参数列表
	 * @return mixed
	 * @since 0.0.3
	 */
	public function invoke($object, $methodName, $arguments = array()) {
		if (!is_object($object)) {
			throw new IReflectionException("can't call a method '{$methodName}' on a non-object");
		}
		if (!($object instanceof $this->className)) {
			throw new IReflectionException("object is not an instance of {$this->className}");
		}
		if (!is_array($arguments)) {
			throw new IReflectionException("arguments must be an array");
		}
		$method = $this->class->getMethod($methodName);
		$parameters = $method->getParameters();
		$needNumber = 0;
		foreach ($parameters as $parameter) {
			if (!$parameter->isOptional()) {
				$needNumber ++;
			}
		}
		if (count($arguments) < $needNumber) {
			throw new IReflectionTooFewArgumentsException("method '{$this->className}::{$methodName}' need {$needNumber} arguments, " . count($arguments) . " gived");
		}
		try {
			return $method->invokeArgs($object, $arguments);
		} catch (ReflectionException $e) {
			throw new IReflectionException($e->getMessage());
		}
	}
	
	/**
	 * 取得对象的属性
	 * 
	 * 依次尝试查找
	 * <pre>
	 * object[property]
	 * $object->property
	 * $object->getProperty()
	 * $object->get(property)
	 * </pre>
	 * 直至找到为止
	 * 
	 * @param mixed $object array/object 
	 * @param string $property 属性
	 * @return mixed
	 * @since 0.1
	 */
	public static function getProperty($object, $property) {
		if (is_array($object)) {
			if (array_key_exists($property, $object)) {
				return $object[$property];
			}
			else {
				return null;
			}
		}
		
		if (!is_string($property)) {
			throw new IReflectionException("invalid property");
		}
		if (!is_object($object)) {
			throw new IReflectionException("can't get property '{$property}' from " . getType($object) . " (" . var_export($object, true). ")");
		}
		
		//from vars
		$vars = get_object_vars($object);
		if (array_key_exists($property, $vars)) {
			return $vars[$property];
		}
		
		//from getter
		$className = get_class($object);
		$reflect = new IReflection($className);
		$getter = "get" . $property;
		if (method_exists($object, $getter)) {
			return $reflect->invoke($object, $getter);
		}
		
		//from get
		if (method_exists($object, "get")) {
			return  $reflect->invoke($object, "get", array($property));
		}
		
		throw new IReflectionException("can't find getter method for property '{$property}' in '{$className}'");		
	}
	
	/**
	 * 设置对象属性
	 *
	 * @param mixed $object 对象
	 * @param string $property 属性
	 * @param string $value 值
	 * @return boolean
	 * @since 0.1
	 */
	public static function setProperty($object, $property, $value) {
		if (is_array($object)) {
			$object[$property] = $value;
			return true;
		}
		if (!is_string($property)) {
			throw new IReflectionException("invalid property");
		}
		if (!is_object($object)) {
			throw new IReflectionException("can't set property '{$property}' from " . getType($object) . " (" . var_export($object, true). ")");
		}
		
		//from vars
		$vars = get_object_vars($object);
		if (array_key_exists($property, $vars)) {
			$object->$property = $value;
			return true;
		}
		
		//from setter
		$className = get_class($object);
		$reflect = new IReflection($className);
		$setter = "set" . $property;
		if (method_exists($object, $setter)) {
			$reflect->invoke($object, $setter, array($value));
			return true;
		}
		
		//from set
		if (method_exists($object, "set")) {
			$reflect->invoke($object, "set", array($property, $value));
			return true;
		}
		
		//是否是私有的属性
		try {
			$reflectProperty = $reflect->getClass()->getProperty($property);
			if ($reflectProperty->isPublic()) {
				$reflectProperty->setValue($object, $value);
			}
			else if ($reflectProperty->isStatic()) {
				eval("{$className}::\$property = \$value");
			}
			else {
				throw new IReflectionException("can't find setter method for property '{$property}' in '{$className}'");
			}
		} catch(ReflectionException $e) {
			$object->$property = $value;
		}
		
		return false;
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $object
	 * @param unknown_type $property
	 * @since 0.1
	 */
	public static function getPropertyChain($object, $property) {
		$props = explode(".", $property);
		$subObject = self::getProperty($object, $props[0]);
		unset($props[0]);
		if (!empty($props)) {
			return self::getPropertyChain($subObject, implode(".", $props));
		}
		else {
			return $subObject;
		}
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $object
	 * @param unknown_type $property
	 * @param unknown_type $value
	 * @return unknown
	 * @since 0.1
	 */
	public static function setPropertyChain($object, $property, $value) {
		$props = explode(".", $property);
		if (count($props) == 1) {
			return self::setProperty($object, $property, $value);
		}
		$lastProperty = array_pop($props);
		$subObject = self::getPropertyChain($object, implode(".", $props));
		return self::setProperty($subObject, $lastProperty, $value);
	}
}

?>